{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Pandas 字符串清理和空白处理\n",
        "\n",
        "本教程介绍Pandas中字符串清理和空白处理的相关知识点和代码案例，包括去除空白字符、移除前缀后缀等操作。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 学习目标\n",
        "\n",
        "- 掌握使用 `str.strip()` 去除字符串两端空白\n",
        "- 掌握使用 `str.lstrip()` 去除字符串左侧空白\n",
        "- 掌握使用 `str.rstrip()` 去除字符串右侧空白\n",
        "- 掌握使用 `str.removeprefix()` 移除字符串前缀\n",
        "- 掌握使用 `str.removesuffix()` 移除字符串后缀\n",
        "- 了解这些方法在实际数据处理中的应用场景\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 1. 导入库\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "import numpy as np\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2. 去除空白字符\n",
        "\n",
        "在处理数据时，字符串中经常包含不必要的空白字符（空格、制表符、换行符等）。Pandas提供了三种方法来去除这些空白字符。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.1 str.strip() - 去除两端空白\n",
        "\n",
        "`str.strip()` 方法用于去除字符串两端的空白字符（包括空格、制表符 `\\t`、换行符 `\\n` 等）。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据:\n",
            "0          Python  \n",
            "1        \\tPandas\\n\n",
            "2           NumPy  \n",
            "3    \\nMatplotlib\\t\n",
            "4         Seaborn  \n",
            "dtype: object\n",
            "\n",
            "数据类型: object\n"
          ]
        }
      ],
      "source": [
        "# 创建包含空白字符的Series\n",
        "data = pd.Series(['  Python  ', '\\tPandas\\n', '  NumPy  ', '\\nMatplotlib\\t', '  Seaborn  '])\n",
        "print(\"原始数据:\")\n",
        "print(data)\n",
        "print(\"\\n数据类型:\", data.dtype)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "去除两端空白后:\n",
            "0        Python\n",
            "1        Pandas\n",
            "2         NumPy\n",
            "3    Matplotlib\n",
            "4       Seaborn\n",
            "dtype: object\n",
            "\n",
            "对比:\n",
            "'  Python  ' -> 'Python'\n",
            "'\tPandas\n",
            "' -> 'Pandas'\n",
            "'  NumPy  ' -> 'NumPy'\n",
            "'\n",
            "Matplotlib\t' -> 'Matplotlib'\n",
            "'  Seaborn  ' -> 'Seaborn'\n"
          ]
        }
      ],
      "source": [
        "# 使用strip()去除两端空白\n",
        "cleaned = data.str.strip()\n",
        "print(\"去除两端空白后:\")\n",
        "print(cleaned)\n",
        "print(\"\\n对比:\")\n",
        "for i in range(len(data)):\n",
        "    print(f\"'{data.iloc[i]}' -> '{cleaned.iloc[i]}'\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.2 str.lstrip() - 去除左侧空白\n",
        "\n",
        "`str.lstrip()` 方法用于去除字符串左侧（开头）的空白字符。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据:\n",
            "0          Python\n",
            "1        \\tPandas\n",
            "2           NumPy\n",
            "3    \\nMatplotlib\n",
            "4         Seaborn\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建包含左侧空白的Series\n",
        "data_left = pd.Series(['  Python', '\\tPandas', '  NumPy', '\\nMatplotlib', '  Seaborn'])\n",
        "print(\"原始数据:\")\n",
        "print(data_left)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "去除左侧空白后:\n",
            "0        Python\n",
            "1        Pandas\n",
            "2         NumPy\n",
            "3    Matplotlib\n",
            "4       Seaborn\n",
            "dtype: object\n",
            "\n",
            "对比:\n",
            "'  Python' -> 'Python'\n",
            "'\tPandas' -> 'Pandas'\n",
            "'  NumPy' -> 'NumPy'\n",
            "'\n",
            "Matplotlib' -> 'Matplotlib'\n",
            "'  Seaborn' -> 'Seaborn'\n"
          ]
        }
      ],
      "source": [
        "# 使用lstrip()去除左侧空白\n",
        "cleaned_left = data_left.str.lstrip()\n",
        "print(\"去除左侧空白后:\")\n",
        "print(cleaned_left)\n",
        "print(\"\\n对比:\")\n",
        "for i in range(len(data_left)):\n",
        "    print(f\"'{data_left.iloc[i]}' -> '{cleaned_left.iloc[i]}'\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.3 str.rstrip() - 去除右侧空白\n",
        "\n",
        "`str.rstrip()` 方法用于去除字符串右侧（结尾）的空白字符。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据:\n",
            "0        Python  \n",
            "1        Pandas\\t\n",
            "2         NumPy\\n\n",
            "3    Matplotlib  \n",
            "4       Seaborn\\t\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建包含右侧空白的Series\n",
        "data_right = pd.Series(['Python  ', 'Pandas\\t', 'NumPy\\n', 'Matplotlib  ', 'Seaborn\\t'])\n",
        "print(\"原始数据:\")\n",
        "print(data_right)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "去除右侧空白后:\n",
            "0        Python\n",
            "1        Pandas\n",
            "2         NumPy\n",
            "3    Matplotlib\n",
            "4       Seaborn\n",
            "dtype: object\n",
            "\n",
            "对比:\n",
            "'Python  ' -> 'Python'\n",
            "'Pandas\t' -> 'Pandas'\n",
            "'NumPy\n",
            "' -> 'NumPy'\n",
            "'Matplotlib  ' -> 'Matplotlib'\n",
            "'Seaborn\t' -> 'Seaborn'\n"
          ]
        }
      ],
      "source": [
        "# 使用rstrip()去除右侧空白\n",
        "cleaned_right = data_right.str.rstrip()\n",
        "print(\"去除右侧空白后:\")\n",
        "print(cleaned_right)\n",
        "print(\"\\n对比:\")\n",
        "for i in range(len(data_right)):\n",
        "    print(f\"'{data_right.iloc[i]}' -> '{cleaned_right.iloc[i]}'\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 2.4 自定义去除字符\n",
        "\n",
        "`strip()`, `lstrip()`, `rstrip()` 方法还可以接受参数，指定要去除的特定字符。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据:\n",
            "0        ***Python***\n",
            "1        ---Pandas---\n",
            "2         ###NumPy###\n",
            "3    +++Matplotlib+++\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建包含特定字符的Series\n",
        "data_custom = pd.Series(['***Python***', '---Pandas---', '###NumPy###', '+++Matplotlib+++'])\n",
        "print(\"原始数据:\")\n",
        "print(data_custom)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "去除 '*' 字符:\n",
            "0              Python\n",
            "1        ---Pandas---\n",
            "2         ###NumPy###\n",
            "3    +++Matplotlib+++\n",
            "dtype: object\n",
            "\n",
            "去除 '-' 字符:\n",
            "0        ***Python***\n",
            "1              Pandas\n",
            "2         ###NumPy###\n",
            "3    +++Matplotlib+++\n",
            "dtype: object\n",
            "\n",
            "去除 '#' 字符:\n",
            "0        ***Python***\n",
            "1        ---Pandas---\n",
            "2               NumPy\n",
            "3    +++Matplotlib+++\n",
            "dtype: object\n",
            "\n",
            "去除 '+' 字符:\n",
            "0    ***Python***\n",
            "1    ---Pandas---\n",
            "2     ###NumPy###\n",
            "3      Matplotlib\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 去除特定的字符\n",
        "print(\"去除 '*' 字符:\")\n",
        "print(data_custom.str.strip('*'))\n",
        "\n",
        "print(\"\\n去除 '-' 字符:\")\n",
        "print(data_custom.str.strip('-'))\n",
        "\n",
        "print(\"\\n去除 '#' 字符:\")\n",
        "print(data_custom.str.strip('#'))\n",
        "\n",
        "print(\"\\n去除 '+' 字符:\")\n",
        "print(data_custom.str.strip('+'))\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 3. 移除前缀和后缀\n",
        "\n",
        "Python 3.9+ 引入了 `removeprefix()` 和 `removesuffix()` 方法，用于移除字符串的前缀和后缀。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 3.1 str.removeprefix() - 移除前缀\n",
        "\n",
        "`str.removeprefix()` 方法用于移除字符串的指定前缀。如果字符串不以该前缀开头，则返回原字符串。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据:\n",
            "0       Mr. Smith\n",
            "1    Mrs. Johnson\n",
            "2       Dr. Brown\n",
            "3       Ms. Davis\n",
            "4    Prof. Wilson\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建包含前缀的Series\n",
        "data_prefix = pd.Series(['Mr. Smith', 'Mrs. Johnson', 'Dr. Brown', 'Ms. Davis', 'Prof. Wilson'])\n",
        "print(\"原始数据:\")\n",
        "print(data_prefix)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "移除 'Mr. ' 前缀:\n",
            "0           Smith\n",
            "1    Mrs. Johnson\n",
            "2       Dr. Brown\n",
            "3       Ms. Davis\n",
            "4    Prof. Wilson\n",
            "dtype: object\n",
            "\n",
            "对比:\n",
            "'Mr. Smith' -> 'Smith'\n",
            "'Mrs. Johnson' -> 'Mrs. Johnson'\n",
            "'Dr. Brown' -> 'Dr. Brown'\n",
            "'Ms. Davis' -> 'Ms. Davis'\n",
            "'Prof. Wilson' -> 'Prof. Wilson'\n"
          ]
        }
      ],
      "source": [
        "# 移除 'Mr. ' 前缀\n",
        "print(\"移除 'Mr. ' 前缀:\")\n",
        "result1 = data_prefix.str.removeprefix('Mr. ')\n",
        "print(result1)\n",
        "print(\"\\n对比:\")\n",
        "for i in range(len(data_prefix)):\n",
        "    print(f\"'{data_prefix.iloc[i]}' -> '{result1.iloc[i]}'\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "移除 'Mr. ', 'Mrs. ', 'Dr. ', 'Ms. ', 'Prof. ' 前缀:\n",
            "0      Smith\n",
            "1    Johnson\n",
            "2      Brown\n",
            "3      Davis\n",
            "4     Wilson\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 移除多个不同的前缀\n",
        "print(\"移除 'Mr. ', 'Mrs. ', 'Dr. ', 'Ms. ', 'Prof. ' 前缀:\")\n",
        "result2 = (\n",
        "    data_prefix.str.removeprefix('Mr. ')\n",
        "              .str.removeprefix('Mrs. ')\n",
        "              .str.removeprefix('Dr. ')\n",
        "              .str.removeprefix('Ms. ')\n",
        "              .str.removeprefix('Prof. ')\n",
        ")\n",
        "print(result2)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "使用正则表达式移除前缀:\n",
            "0      Smith\n",
            "1    Johnson\n",
            "2      Brown\n",
            "3      Davis\n",
            "4     Wilson\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 使用正则表达式处理多个前缀（更灵活的方法）\n",
        "import re\n",
        "\n",
        "# 使用replace方法配合正则表达式\n",
        "result3 = data_prefix.str.replace(r'^(Mr\\.|Mrs\\.|Dr\\.|Ms\\.|Prof\\.)\\s+', '', regex=True)\n",
        "print(\"使用正则表达式移除前缀:\")\n",
        "print(result3)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 3.2 str.removesuffix() - 移除后缀\n",
        "\n",
        "`str.removesuffix()` 方法用于移除字符串的指定后缀。如果字符串不以该后缀结尾，则返回原字符串。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据:\n",
            "0        file.txt\n",
            "1    document.pdf\n",
            "2       image.jpg\n",
            "3        data.csv\n",
            "4     report.docx\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建包含后缀的Series\n",
        "data_suffix = pd.Series(['file.txt', 'document.pdf', 'image.jpg', 'data.csv', 'report.docx'])\n",
        "print(\"原始数据:\")\n",
        "print(data_suffix)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "移除 '.txt' 后缀:\n",
            "0            file\n",
            "1    document.pdf\n",
            "2       image.jpg\n",
            "3        data.csv\n",
            "4     report.docx\n",
            "dtype: object\n",
            "\n",
            "对比:\n",
            "'file.txt' -> 'file'\n",
            "'document.pdf' -> 'document.pdf'\n",
            "'image.jpg' -> 'image.jpg'\n",
            "'data.csv' -> 'data.csv'\n",
            "'report.docx' -> 'report.docx'\n"
          ]
        }
      ],
      "source": [
        "# 移除 '.txt' 后缀\n",
        "print(\"移除 '.txt' 后缀:\")\n",
        "result1 = data_suffix.str.removesuffix('.txt')\n",
        "print(result1)\n",
        "print(\"\\n对比:\")\n",
        "for i in range(len(data_suffix)):\n",
        "    print(f\"'{data_suffix.iloc[i]}' -> '{result1.iloc[i]}'\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "移除所有文件扩展名:\n",
            "0        file\n",
            "1    document\n",
            "2       image\n",
            "3        data\n",
            "4      report\n",
            "dtype: object\n",
            "\n",
            "对比:\n",
            "'file.txt' -> 'file'\n",
            "'document.pdf' -> 'document'\n",
            "'image.jpg' -> 'image'\n",
            "'data.csv' -> 'data'\n",
            "'report.docx' -> 'report'\n"
          ]
        }
      ],
      "source": [
        "# 移除文件扩展名（使用正则表达式更灵活）\n",
        "print(\"移除所有文件扩展名:\")\n",
        "result2 = data_suffix.str.replace(r'\\.[^.]+$', '', regex=True)\n",
        "print(result2)\n",
        "print(\"\\n对比:\")\n",
        "for i in range(len(data_suffix)):\n",
        "    print(f\"'{data_suffix.iloc[i]}' -> '{result2.iloc[i]}'\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 4. 实际应用案例\n",
        "\n",
        "让我们通过一个完整的案例来演示这些方法在实际数据处理中的应用。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 案例：清理用户输入数据\n",
        "\n",
        "假设我们有一个包含用户输入数据的DataFrame，需要清理其中的空白字符和前缀后缀。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据:\n",
            "              name                 email        phone username\n",
            "0       John Doe      john@example.com    +1-555-1234   @user1\n",
            "1   \\tJane Smith\\n    jane@example.com\\t  +1-555-5678   @user2\n",
            "2    Bob Johnson       bob@example.com    +1-555-9012   @user3\n",
            "3  \\nAlice Brown\\t   \\nalice@example.com  +1-555-3456   @user4\n",
            "\n",
            "数据类型:\n",
            "name        object\n",
            "email       object\n",
            "phone       object\n",
            "username    object\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建模拟的用户数据\n",
        "df = pd.DataFrame({\n",
        "    'name': ['  John Doe  ', '\\tJane Smith\\n', '  Bob Johnson  ', '\\nAlice Brown\\t'],\n",
        "    'email': ['  john@example.com  ', 'jane@example.com\\t', '  bob@example.com  ', '\\nalice@example.com'],\n",
        "    'phone': ['+1-555-1234', '+1-555-5678', '+1-555-9012', '+1-555-3456'],\n",
        "    'username': ['@user1', '@user2', '@user3', '@user4']\n",
        "})\n",
        "\n",
        "print(\"原始数据:\")\n",
        "print(df)\n",
        "print(\"\\n数据类型:\")\n",
        "print(df.dtypes)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "清理后的数据:\n",
            "          name              email     phone username\n",
            "0     John Doe   john@example.com  555-1234    user1\n",
            "1   Jane Smith   jane@example.com  555-5678    user2\n",
            "2  Bob Johnson    bob@example.com  555-9012    user3\n",
            "3  Alice Brown  alice@example.com  555-3456    user4\n"
          ]
        }
      ],
      "source": [
        "# 清理数据\n",
        "df_cleaned = df.copy()\n",
        "\n",
        "# 清理name列：去除两端空白\n",
        "df_cleaned['name'] = df_cleaned['name'].str.strip()\n",
        "\n",
        "# 清理email列：去除两端空白\n",
        "df_cleaned['email'] = df_cleaned['email'].str.strip()\n",
        "\n",
        "# 清理phone列：移除 '+1-' 前缀\n",
        "df_cleaned['phone'] = df_cleaned['phone'].str.removeprefix('+1-')\n",
        "\n",
        "# 清理username列：移除 '@' 前缀\n",
        "df_cleaned['username'] = df_cleaned['username'].str.removeprefix('@')\n",
        "\n",
        "print(\"清理后的数据:\")\n",
        "print(df_cleaned)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "对比清理前后:\n",
            "============================================================\n",
            "\n",
            "name 列:\n",
            "  原始: '  John Doe  '\n",
            "  清理: 'John Doe'\n",
            "\n",
            "  原始: '\tJane Smith\n",
            "'\n",
            "  清理: 'Jane Smith'\n",
            "\n",
            "  原始: '  Bob Johnson  '\n",
            "  清理: 'Bob Johnson'\n",
            "\n",
            "  原始: '\n",
            "Alice Brown\t'\n",
            "  清理: 'Alice Brown'\n",
            "\n",
            "\n",
            "email 列:\n",
            "  原始: '  john@example.com  '\n",
            "  清理: 'john@example.com'\n",
            "\n",
            "  原始: 'jane@example.com\t'\n",
            "  清理: 'jane@example.com'\n",
            "\n",
            "  原始: '  bob@example.com  '\n",
            "  清理: 'bob@example.com'\n",
            "\n",
            "  原始: '\n",
            "alice@example.com'\n",
            "  清理: 'alice@example.com'\n",
            "\n",
            "\n",
            "phone 列:\n",
            "  原始: '+1-555-1234'\n",
            "  清理: '555-1234'\n",
            "\n",
            "  原始: '+1-555-5678'\n",
            "  清理: '555-5678'\n",
            "\n",
            "  原始: '+1-555-9012'\n",
            "  清理: '555-9012'\n",
            "\n",
            "  原始: '+1-555-3456'\n",
            "  清理: '555-3456'\n",
            "\n",
            "\n",
            "username 列:\n",
            "  原始: '@user1'\n",
            "  清理: 'user1'\n",
            "\n",
            "  原始: '@user2'\n",
            "  清理: 'user2'\n",
            "\n",
            "  原始: '@user3'\n",
            "  清理: 'user3'\n",
            "\n",
            "  原始: '@user4'\n",
            "  清理: 'user4'\n",
            "\n"
          ]
        }
      ],
      "source": [
        "# 对比清理前后的数据\n",
        "print(\"\\n对比清理前后:\")\n",
        "print(\"=\" * 60)\n",
        "for col in df.columns:\n",
        "    print(f\"\\n{col} 列:\")\n",
        "    for i in range(len(df)):\n",
        "        print(f\"  原始: '{df[col].iloc[i]}'\")\n",
        "        print(f\"  清理: '{df_cleaned[col].iloc[i]}'\")\n",
        "        print()\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 案例：处理文件路径\n",
        "\n",
        "另一个常见的应用场景是处理文件路径，移除路径前缀或文件扩展名。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始文件路径:\n",
            "0    /home/user/documents/file1.txt\n",
            "1    /home/user/documents/file2.pdf\n",
            "2      /home/user/images/photo1.jpg\n",
            "3      /home/user/images/photo2.png\n",
            "4         /home/user/data/data1.csv\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 创建包含文件路径的Series\n",
        "file_paths = pd.Series([\n",
        "    '/home/user/documents/file1.txt',\n",
        "    '/home/user/documents/file2.pdf',\n",
        "    '/home/user/images/photo1.jpg',\n",
        "    '/home/user/images/photo2.png',\n",
        "    '/home/user/data/data1.csv'\n",
        "])\n",
        "\n",
        "print(\"原始文件路径:\")\n",
        "print(file_paths)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "移除 '/home/user/' 前缀:\n",
            "0    documents/file1.txt\n",
            "1    documents/file2.pdf\n",
            "2      images/photo1.jpg\n",
            "3      images/photo2.png\n",
            "4         data/data1.csv\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 移除路径前缀\n",
        "print(\"移除 '/home/user/' 前缀:\")\n",
        "relative_paths = file_paths.str.removeprefix('/home/user/')\n",
        "print(relative_paths)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "移除文件扩展名:\n",
            "0    /home/user/documents/file1\n",
            "1    /home/user/documents/file2\n",
            "2      /home/user/images/photo1\n",
            "3      /home/user/images/photo2\n",
            "4         /home/user/data/data1\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 移除文件扩展名\n",
        "print(\"移除文件扩展名:\")\n",
        "file_names = file_paths.str.replace(r'\\.[^.]+$', '', regex=True)\n",
        "print(file_names)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "只获取文件名（不包含路径和扩展名）:\n",
            "0     file1\n",
            "1     file2\n",
            "2    photo1\n",
            "3    photo2\n",
            "4     data1\n",
            "dtype: object\n"
          ]
        }
      ],
      "source": [
        "# 只获取文件名（不包含路径和扩展名）\n",
        "print(\"只获取文件名（不包含路径和扩展名）:\")\n",
        "base_names = file_paths.str.split('/').str[-1].str.replace(r'\\.[^.]+$', '', regex=True)\n",
        "print(base_names)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 5. 处理NaN值\n",
        "\n",
        "当Series中包含NaN值时，这些字符串方法会正确处理NaN值，返回NaN而不是抛出错误。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "原始数据（包含NaN）:\n",
            "0      Python  \n",
            "1          None\n",
            "2       NumPy  \n",
            "3           NaN\n",
            "4      Pandas  \n",
            "dtype: object\n",
            "\n",
            "是否包含NaN: True\n"
          ]
        }
      ],
      "source": [
        "# 创建包含NaN值的Series\n",
        "data_with_nan = pd.Series(['  Python  ', None, '  NumPy  ', np.nan, '  Pandas  '])\n",
        "print(\"原始数据（包含NaN）:\")\n",
        "print(data_with_nan)\n",
        "print(\"\\n是否包含NaN:\", data_with_nan.isna().any())\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "去除空白后（NaN值保持不变）:\n",
            "0    Python\n",
            "1      None\n",
            "2     NumPy\n",
            "3       NaN\n",
            "4    Pandas\n",
            "dtype: object\n",
            "\n",
            "NaN值数量: 2\n"
          ]
        }
      ],
      "source": [
        "# 使用strip()处理包含NaN的数据\n",
        "cleaned_with_nan = data_with_nan.str.strip()\n",
        "print(\"去除空白后（NaN值保持不变）:\")\n",
        "print(cleaned_with_nan)\n",
        "print(\"\\nNaN值数量:\", cleaned_with_nan.isna().sum())\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 6. 知识点总结\n",
        "\n",
        "### 6.1 去除空白字符的方法\n",
        "\n",
        "| 方法 | 功能 | 说明 |\n",
        "|------|------|------|\n",
        "| `str.strip()` | 去除两端空白 | 去除字符串开头和结尾的空白字符（空格、制表符、换行符等） |\n",
        "| `str.lstrip()` | 去除左侧空白 | 只去除字符串开头的空白字符 |\n",
        "| `str.rstrip()` | 去除右侧空白 | 只去除字符串结尾的空白字符 |\n",
        "\n",
        "### 6.2 移除前缀和后缀的方法\n",
        "\n",
        "| 方法 | 功能 | 说明 |\n",
        "|------|------|------|\n",
        "| `str.removeprefix(prefix)` | 移除前缀 | 如果字符串以指定前缀开头，则移除该前缀；否则返回原字符串 |\n",
        "| `str.removesuffix(suffix)` | 移除后缀 | 如果字符串以指定后缀结尾，则移除该后缀；否则返回原字符串 |\n",
        "\n",
        "### 6.3 注意事项\n",
        "\n",
        "1. **Python版本要求**：`removeprefix()` 和 `removesuffix()` 方法需要 Python 3.9+ 版本\n",
        "2. **NaN值处理**：所有字符串方法都会正确处理NaN值，返回NaN而不是抛出错误\n",
        "3. **自定义字符**：`strip()`, `lstrip()`, `rstrip()` 可以接受参数指定要去除的特定字符\n",
        "4. **性能**：这些方法都是向量化的，对大型数据集处理效率很高\n",
        "5. **链式调用**：可以链式调用多个字符串方法进行复杂的数据清理操作\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 7. 练习题\n",
        "\n",
        "尝试完成以下练习，巩固所学知识：\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 练习1：清理产品名称\n",
        "\n",
        "给定一个包含产品名称的Series，清理其中的空白字符：\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 练习数据\n",
        "products = pd.Series(['  Laptop Computer  ', '\\tSmartphone\\n', '  Tablet Device  ', '\\nHeadphones\\t'])\n",
        "\n",
        "# 在这里编写代码清理产品名称\n",
        "# 提示：使用 strip() 方法\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 练习2：移除URL前缀\n",
        "\n",
        "给定一个包含完整URL的Series，提取域名部分：\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 练习数据\n",
        "urls = pd.Series([\n",
        "    'https://www.example.com/page1',\n",
        "    'http://www.example.com/page2',\n",
        "    'https://www.google.com/search',\n",
        "    'http://www.github.com/user'\n",
        "])\n",
        "\n",
        "# 在这里编写代码移除 'https://' 和 'http://' 前缀\n",
        "# 提示：可以链式调用 removeprefix() 方法\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 练习3：清理用户数据\n",
        "\n",
        "给定一个包含用户数据的DataFrame，清理所有字符串列：\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 练习数据\n",
        "user_data = pd.DataFrame({\n",
        "    'first_name': ['  John  ', '\\tJane\\n', '  Bob  '],\n",
        "    'last_name': ['  Doe  ', 'Smith\\t', '\\nJohnson  '],\n",
        "    'email': ['  john@email.com  ', 'jane@email.com\\t', '  bob@email.com  '],\n",
        "    'city': ['New York  ', '  Los Angeles', '  Chicago  ']\n",
        "})\n",
        "\n",
        "# 在这里编写代码清理所有字符串列\n",
        "# 提示：可以使用 apply() 方法对每列应用 strip()\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "ml311",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.11.0"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
